package module

import (
	"context"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"platon-tools/go-service/common/util"
	"platon-tools/go-service/service"
	"sync"
	"sync/atomic"
	"time"

	logger "platon-tools/go-service/go-logger"
)

var (
	RedisAddr string = "localhost:6379"
	MaxIdle          = 5
)

func newPool(addr string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     MaxIdle,
		IdleTimeout: 240 * time.Second,
		Dial:        func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
	}
}

type RedisDB struct {
	*redis.Pool
	sync.WaitGroup
	ctx    context.Context
	cancel context.CancelFunc

	service     *service.Service
	latestBlock uint64

	running int32
}

func (r *RedisDB) Start(ctx context.Context, config *service.Config, s *service.Service) error {

	fmt.Println("redis init start")

	selfCtx, cancel := context.WithCancel(ctx)
	*r = RedisDB{
		Pool:        newPool(fmt.Sprintf("%s:%d", config.Redis.IP, config.Redis.Port)),
		ctx:         selfCtx,
		cancel:      cancel,
		service:     s,
		latestBlock: 0,
	}

	atomic.StoreInt32(&r.running, 1)
	init := func() {
		conn := r.Get()
		defer conn.Close()
	}

	init()
	go r.runLoop()

	fmt.Println("redis init success")

	return nil
}

func (r *RedisDB) runLoop() {
	defer r.Close()

	sugar := logger.Logger.Sugar()

	for {
		select {
		case <-r.ctx.Done():
			sugar.Infof("redis db event loop quit timer: %s", time.Now().String())
			return
		}
	}
}

func (r *RedisDB) IsRunning() bool {
	if atomic.LoadInt32(&r.running) == 1 {
		return true
	}

	return false
}

func (r *RedisDB) Name() string {
	return "REDIS"
}

func (r *RedisDB) Close() error {
	if r.IsRunning() {
		atomic.StoreInt32(&r.running, 0)
		r.Pool.Close()
		r.cancel()
	}

	return nil
}

func (r *RedisDB) SetSession(key string, value string) error {
	_, err := r.SetStringExpire(key, value, 3600)
	return err
}

func (r *RedisDB) GetString(key string) (string, error) {
	val, err := redis.String(r.Get().Do("GET", key))
	if err != nil {
		return "", err
	}

	return val, nil
}

func (r *RedisDB) SetStringExpire(key string, value string, expireTime int) (string, error) {
	val, err := redis.String(r.Get().Do("set", key, value, "EX", util.ToString(expireTime)))
	if err != nil {
		return "", err
	}

	return val, nil
}

func (r *RedisDB) ZAdd(key string, value string, score interface{}) (string, error) {
	val, err := redis.String(r.Get().Do("zadd", key, score, value))
	if err != nil {
		return "", err
	}

	return val, nil
}

func (r *RedisDB) ZRangeWithScores(key string, start int, end int) (scores []string, values []string, err error) {
	val, err := redis.Values(r.Get().Do("zrange", key, start, end, "withscores"))
	if err != nil {
		return
	}

	for index, v := range val {
		if index%2 == 0 {
			values = append(values, string(v.([]byte)))
		} else {
			scores = append(scores, string(v.([]byte)))
		}
	}

	return
}

func (r *RedisDB) Del(key string) error {
	_, err := r.Get().Do("DEL", key)
	if err != nil {
		return err
	}

	return nil
}
